#include "alu.h"
#include "register.h"
#include "registerset.h"
#include "adders.h"
#include "gates.h"
#include "../cpu/cpu.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#define BUFFER_SIZE 1024

/*
 * void alu_set_flags(Alu *alu);
 * Sets the processor state register flags outside the ALU after an operation has occurred. This
 * operation is undefined if no operations have occurred.
 * alu: a pointer to the ALU
 */
void set_flags(Alu *alu, Bit carry, Bit overflow)
{
	Bit zero;
	Bit negative;
	
	Register psr = cpu_get_state(alu->cpu);

	register_set(&psr, 0);
	
	zero = (register_get(&alu->result_r) == 0) ? TRUE : FALSE;
	negative = register_get_bit(&alu->result_r, REGISTER_BITS - 1);
	
	register_set_bit(&psr, P_BIT_POS, ~negative);
	register_set_bit(&psr, Z_BIT_POS, zero);
	register_set_bit(&psr, N_BIT_POS, negative);
	register_set_bit(&psr, O_BIT_POS, overflow);
	register_set_bit(&psr, C_BIT_POS, carry);

	cpu_set_state(alu->cpu, psr);
}

/*
 * Alu *new_alu(Cpu *cpu);
 * Constructs a new ALU and returns a pointer to it.
 * cpu: a pointer to the processor object, used to set the PSR after an operation.
 */
Alu *new_alu(Cpu *cpu)
{
	Alu *alu = (Alu *)malloc(sizeof(Alu));
	alu->cpu = cpu;
	
	return alu;
}

/*
 * void alu_set_operand_a(Alu *alu, unsigned short value);
 * Sets the contents of the ALU's operand A register.
 * alu: a pointer to the ALU
 * value: the value to set
 */
void alu_set_operand_a(Alu *alu, unsigned short value)
{
	register_set(&alu->operand_a, value);
}

/*
 * void alu_set_operand_b(Alu *alu, unsigned short value);
 * Sets the contents of the ALU's operand B register.
 * alu: a pointer to the ALU
 * value: the value to set
 */
void alu_set_operand_b(Alu *alu, unsigned short value)
{
	register_set(&alu->operand_b, value);
}

/*
 * unsigned short alu_get_result_r(Alu *alu);
 * Gets the value of the ALU's result register.
 * alu: a pointer to the ALU
 */
unsigned short alu_get_result_r(Alu *alu)
{
	return register_get(&alu->result_r);
}

/*
 * void alu_neg(Alu *alu);
 * Performs a negation operation with the ALU's operand A and stores the result in the result
 * register.
 * alu: a pointer to the ALU
 */
void alu_neg(Alu *alu)
{
	alu_not(alu);
	alu->operand_a = alu->result_r;
	alu->operand_b = 1;
	alu_add(alu);
	set_flags(alu, 0, 0);
}

/*
 * void alu_add(Alu *alu);
 * Performs an addition operation with the ALU's operands A and B and stores the result in the
 * result register.
 * alu: a pointer to the ALU
 */
void alu_add(Alu *alu)
{
	Bit carry;
	Bit overflow;
	
	RippleCarryAdderPtr adder = rippleCarryAdderConstruct();
	rippleCarryAdderSetRegisters(adder, alu->operand_a, alu->operand_b);
	rippleCarryAdderAdd(adder);
	alu->result_r = rippleCarryAdderGetResult(adder);
	
	carry = (adder->flags & 0x2) ? TRUE : FALSE;
	overflow = (adder->flags & 0x1) ? TRUE : FALSE;
	
	set_flags(alu, carry, overflow);
}

/*
 * void alu_sub(Alu *alu);
 * Performs a subtraction operation with the ALU's operands A and B and stores the result in the
 * result register.
 * alu: a pointer to the ALU
 */
void alu_sub(Alu *alu)
{
	Register a = alu->operand_a;
	alu->operand_a = alu->operand_b;
	alu_neg(alu);
	alu->operand_a = a;
	alu->operand_b = alu->result_r;
	alu_add(alu);
}

/*
 * void alu_and(Alu *alu);
 * Performs a logical AND operation with the ALU's operands A and B and stores the result in the
 * result register.
 * alu: a pointer to the ALU
 */
void alu_and(Alu *alu)
{
	int i;
	for (i = 0; i < REGISTER_BITS; i++)
	{
		Bit a = register_get_bit(&alu->operand_a, i);
		Bit b = register_get_bit(&alu->operand_b, i);
		Bit r = and(a, b);
		register_set_bit(&alu->result_r, i, r);
	}
	set_flags(alu, 0, 0);
}

/*
 * void alu_or(Alu *alu);
 * Performs a logical OR operation with the ALU's operands A and B and stores the result in the
 * result register.
 * alu: a pointer to the ALU
 */
void alu_or(Alu *alu)
{
	int i;
	for (i = 0; i < REGISTER_BITS; i++)
	{
		Bit a = register_get_bit(&alu->operand_a, i);
		Bit b = register_get_bit(&alu->operand_b, i);
		Bit r = or(a, b);
		register_set_bit(&alu->result_r, i, r);
	}
	set_flags(alu, 0, 0);
}

/*
 * void alu_not(Alu *alu);
 * Performs a logical NOT operation with the ALU's operand A and stores the result in the result
 * register.
 * alu: a pointer to the ALU
 */
void alu_not(Alu *alu)
{
	int i;
	for (i = 0; i < REGISTER_BITS; i++)
	{
		Bit a = register_get_bit(&alu->operand_a, i);
		Bit r = not(a);
		register_set_bit(&alu->result_r, i, r);
	}
	set_flags(alu, 0, 0);
}

/*
 * void delete_alu(Alu *alu);
 * Frees the memory associated with this ALU.
 * alu: a pointer to the ALU to delete
 */
void delete_alu(Alu *alu)
{
	free(alu);
}

/*
 * int get_number(char *message);
 * Gets a single-digit number from the keyboard.
 * message: The prompt to print before getting the number.
 * minimum: The minimum value to accept.
 * maximum: The maximum value to accept.
 */
int get_number(char *message, int minimum, int maximum)
{
	int found;
	int value;
	
	do
	{
		char buffer[BUFFER_SIZE];
		int fields;
		
		printf(message);
		gets(buffer);
		fields = sscanf(buffer, "%d", &value);
		found = (fields == 1 && value >= minimum && value <= maximum);
		
		if (!found)
			printf("Please enter a valid number between %d and %d.\n\n", minimum, maximum);
	}
	while (!found);
	
	return value;
}

/*
 * unsigned short get_register(int index);
 * Gets a register value from the keyboard.
 * index: The index number to print.
 */
unsigned short get_register(int index)
{
	int found;
	unsigned short value;
	
	do
	{
		char buffer[BUFFER_SIZE];
		int fields;
		
		printf("Input register R%d: ", index);
		gets(buffer);
		fields = sscanf(buffer, "%hx", &value);
		found = (fields == 1);
		
		if (!found)
			printf("\nPlease enter a valid hex register value.\n");
	}
	while (!found);
	
	return value;
}

/*
 * void get_registerset(RegisterSet *registerset, int count);
 * Gets values for a number of registers.
 * count: The number of registers to fill.
 */
void get_registerset(RegisterSet *registerset, int count)
{
	int i;
	for (i = 0; i < count; i++)
		registerset_set_register(registerset, i, get_register(i));
}
